﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.Entity;
using System.Diagnostics;
using System.Linq;
using System.Net.Mail;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Web;

namespace BookContent
{
    public enum UserInWorkflowStatus
    {
        NewUser = 1, InActiveUser = 2, SentForVerificationUser = 3, VerifiedUser = 4, BronjMember = 5, SilverMember = 6, GoldMember = 7, DeletedUser = 8
    }
    public enum ConfigurationHelper
    {
        CacheExpirationMinutes = 10,
        CacheExpirationMinutes100 = 100
    }

    public class SimpleFormatter
    {
        string _basePath = "http://localhost:11205/Account/VerifyEmail/";
        public SimpleFormatter(string basePath = "")
        {
            if (string.IsNullOrEmpty(basePath))
            {
                try
                {
                    basePath = ConfigurationManager.AppSettings["RequestUrlVerifyBase"];
                    if (string.IsNullOrEmpty(basePath))
                    {
                        Trace.TraceWarning("Not found any Base Url for Verififcation Email");
                    }
                    else
                    {
                        _basePath = basePath;
                    }

                }
                catch
                {
                }
            }
            else
            {
                _basePath = basePath;
            }
        }
        public static SimpleFormatter EmailFormatter = new SimpleFormatter();
        public string GetEmailBodyForVerification(AppUser suser)
        {
            string link = _basePath + suser.ProviderID + "?chabi=" +  suser.Salt +  "&tale=" + suser.UserName;
            string body = "<p>Hello ##email##, </p> ";
            body += "We have created an account for you.  You are not activated your email, <a href='##link##'>##email## </a>";
            body += "Please <a href='##link##'>click here </a>to activate your membership. <p> Thanks</p> <p> Administrator</p>";

            body = body.Replace("##link##", link);
            body = body.Replace("##email##", suser.Email);
            return body;
        }
    }

    public class SimpleMail
    {
        SmtpClient client;
        public SimpleMail()
        {
            client = new SmtpClient();
        }
        public static SimpleMail Smtp = new SimpleMail();
        public string Send(string from, string to, string subject, string body)
        {
            MailMessage message = new MailMessage();
            message.From = new MailAddress(from);
            message.To.Add( new MailAddress(to) );
            message.Subject = subject;
            message.Body = body;
            message.IsBodyHtml = true;           
            client.Send(message);
            return body;
        }
    }

    public class SimpleCache
    {
        public static SimpleCache Web = new SimpleCache();


        public  void Add<T>(T o, string key) where T : class
        {
            Clear(key);
            HttpContext.Current.Cache.Insert(
                key,
                o,
                null,
                DateTime.Now.AddMinutes(
                    (int)ConfigurationHelper.CacheExpirationMinutes),
                System.Web.Caching.Cache.NoSlidingExpiration);
        }

        public  void Clear(string key)
        {
            HttpContext.Current.Cache.Remove(key);
        }
        public  bool Exists(string key)
        {
            return HttpContext.Current.Cache[key] != null;
        }
        public  T Get<T>(string key) where T : class
        {
            try
            {
                return (T)HttpContext.Current.Cache[key];
            }
            catch
            {
                return null;
            }
        }
    }

    public class SimpleEncryption
    {
        public static SimpleEncryption Base64 = new SimpleEncryption();
        public static MD5Encryption MD5 = new MD5Encryption();
        public string SimpleCode2(string text)
        {
            string code = MD5.EncryptString(text);
            return Convert.ToBase64String(UTF8Encoding.Default.GetBytes(code));
        }

        public string SimpleDeCode2(string text)
        {
            string decode = UTF8Encoding.Default.GetString(Convert.FromBase64String(text));
            return MD5.DecryptString(decode);
        }
        public string UrlCode(string text)
        {
            string code = MD5.EncryptString(text);
            return HttpUtility.HtmlEncode(code);
        }

        public string UrlDeCode(string text)
        {
            string decode = HttpUtility.UrlDecode(text);
            return MD5.DecryptString(decode);
        }
    }
    public class MD5Encryption : SimpleEncryption
    {
        string _password ;
        // Need Some injection
        public MD5Encryption(string password = "HereIsTheComplexity")
        {
            _password = password;
        }
        public string EncryptString(string Message)
        {
            byte[] Results;
            System.Text.UTF8Encoding UTF8 = new System.Text.UTF8Encoding();

            // Step 1. We hash the passphrase using MD5
            // We use the MD5 hash generator as the result is a 128 bit byte array
            // which is a valid length for the TripleDES encoder we use below

            MD5CryptoServiceProvider HashProvider = new MD5CryptoServiceProvider();
            byte[] TDESKey = HashProvider.ComputeHash(UTF8.GetBytes(_password));

            // Step 2. Create a new TripleDESCryptoServiceProvider object
            TripleDESCryptoServiceProvider TDESAlgorithm = new TripleDESCryptoServiceProvider();

            // Step 3. Setup the encoder
            TDESAlgorithm.Key = TDESKey;
            TDESAlgorithm.Mode = CipherMode.ECB;
            TDESAlgorithm.Padding = PaddingMode.PKCS7;

            // Step 4. Convert the input string to a byte[]
            byte[] DataToEncrypt = UTF8.GetBytes(Message);

            // Step 5. Attempt to encrypt the string
            try
            {
                ICryptoTransform Encryptor = TDESAlgorithm.CreateEncryptor();
                Results = Encryptor.TransformFinalBlock(DataToEncrypt, 0, DataToEncrypt.Length);
            }
            finally
            {
                // Clear the TripleDes and Hashprovider services of any sensitive information
                TDESAlgorithm.Clear();
                HashProvider.Clear();
            }

            // Step 6. Return the encrypted string as a base64 encoded string
            return Convert.ToBase64String(Results);
        }

        public string DecryptString(string Message)
        {
            byte[] Results;
            System.Text.UTF8Encoding UTF8 = new System.Text.UTF8Encoding();

            // Step 1. We hash the passphrase using MD5
            // We use the MD5 hash generator as the result is a 128 bit byte array
            // which is a valid length for the TripleDES encoder we use below

            MD5CryptoServiceProvider HashProvider = new MD5CryptoServiceProvider();
            byte[] TDESKey = HashProvider.ComputeHash(UTF8.GetBytes(_password));

            // Step 2. Create a new TripleDESCryptoServiceProvider object
            TripleDESCryptoServiceProvider TDESAlgorithm = new TripleDESCryptoServiceProvider();

            // Step 3. Setup the decoder
            TDESAlgorithm.Key = TDESKey;
            TDESAlgorithm.Mode = CipherMode.ECB;
            TDESAlgorithm.Padding = PaddingMode.PKCS7;

            // Step 4. Convert the input string to a byte[]
            byte[] DataToDecrypt = Convert.FromBase64String(Message);

            // Step 5. Attempt to decrypt the string
            try
            {
                ICryptoTransform Decryptor = TDESAlgorithm.CreateDecryptor();
                Results = Decryptor.TransformFinalBlock(DataToDecrypt, 0, DataToDecrypt.Length);
            }
            finally
            {
                // Clear the TripleDes and Hashprovider services of any sensitive information
                TDESAlgorithm.Clear();
                HashProvider.Clear();
            }

            // Step 6. Return the decrypted string in UTF8 format
            return UTF8.GetString(Results);
        }

    }

    public class Book
    {

        static Book keeper = new Book();

        public static Book Keeper
        {
            get { return Book.keeper; }
            set { Book.keeper = value; }
        }

        static PortalEntities db = new PortalEntities();

        public string SimpleCode(string text)
        {
            return SimpleEncryption.MD5.UrlCode(text);
        }

        public string SimpleDeCode(string text)
        {
            return SimpleEncryption.MD5.UrlDeCode(text);
        }

        public  long AddAppUser(string userName, string userEmail, string provider)
        {
            try
            {
                AppUser user = new AppUser();
                user.DateCreated = DateTime.Now;
                user.ProviderID  = provider;
                user.Email = userEmail;
                user.UserName = userName;                
                user.GlobalID = Guid.NewGuid().ToString();
                user.InWorkflow = (int)UserInWorkflowStatus.NewUser;
                db.AppUsers.Add(user);
                db.SaveChanges();

                SendVerificationNeededEmail(user);
                
                return user.AppUserID ;
            }
            catch (Exception ex) { 
            }
            return  -1;
        }
        public void SendVerificationNeededEmail(AppUser user)
        {
            var saltedUser = BookContent.Book.Keeper.GetSaltedUser(user);
            string body = SimpleFormatter.EmailFormatter.GetEmailBodyForVerification(saltedUser);
            SimpleMail.Smtp.Send("administrator@authentication.com", 
                    user.Email, 
                    "Welcome to App",
                    body);  
        }
        public AppUser GetAppUserFromSalt(string salt, int cachedDuration = 0)
        {
            if (cachedDuration > 0)
            {
                object o = SimpleCache.Web.Get<string>(salt);
                if (o == null)
                {
                    var user = db.AppUsers.Where(u => u.Salt.ToLower() == salt.ToLower()).FirstOrDefault();
                    SimpleCache.Web.Add(user, salt);
                    return user;
                }
                else
                {
                    return (AppUser)o;
                }
            }
            else
            {
                var user = db.AppUsers.Where(u => u.Salt.ToLower() == salt.ToLower()).FirstOrDefault();
                return user;
            }
        }
        public  AppUser GetAppUser(string userEmail, int cachedDuration=0)
        {
            if (cachedDuration > 0)
            {
                object o = SimpleCache.Web.Get<string>(userEmail);
                if (o == null)
                {
                    var user = db.AppUsers.Where(u => u.UserName.ToLower() == userEmail.ToLower()).FirstOrDefault();
                    SimpleCache.Web.Add(user, userEmail);
                    return user;
                }
                else
                {
                    return (AppUser)o;
                }
            }
            else
            {
                var user = db.AppUsers.Where(u => u.UserName.ToLower() == userEmail.ToLower()).FirstOrDefault();
                return user;
            }
        }

        public AppUser GetSaltedUser(AppUser user)
        {
            string secret2 = DateTime.Now.ToUniversalTime().ToString();
            string hashCode = SimpleCode(secret2);
            user.Salt = hashCode.Substring(0,8);
            user.Secret = hashCode;
            SaveAppUser(user);
            return user;
        }
       

        public  long SaveAppUser(AppUser user)
        {
            try
            {
                db.Set<AppUser>().Attach(user);
                db.Entry(user).State = EntityState.Modified;
                db.SaveChanges();
                return user.AppUserID;
            }
            catch (Exception ex)
            {
            }
            return -1;
        }

    }
}
